/*
 * @brief:
 * @Author: TzzzT
 * @Date: 2022-04-05 15:51:56
 * @LastEditTime: 2023-04-18 06:28:42
 * @LastEditors: TzzzT
 */
/*
 * @Description:
 * @Author: TzzzT
 * @Date: 2022-04-05 15:51:56
 * @LastEditTime: 2022-04-21 00:03:06
 * @LastEditors: TzzzT
 */

#include "nvs.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "string.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/semphr.h"
#include "esp_timer.h"

#include "esp_system.h"
#include "esp_mac.h"

#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_continuous.h"
#include "esp_adc/adc_cali.h"

#include "RCS_slave.h"
#include "RCS_protocol.h"

static const char *SYS_TAG = "SYS_TAG";

static const char *SHOCK_TAG = "SHOCK_TAG";
static const char *BATT_TAG = "BATT_TAG";
static const char *GAME_TAG = "GAME_TAG";

extern TaskHandle_t shockTaskHandle;

static SemaphoreHandle_t xSystemMutex = NULL;

rcs_slave_system_t rcsSystem = {
    .device.is_connect_master = DISCONNECT_BLE,
    .game.start = false,
    .game.player.bullet_nums = 0,
    .game.player.damage = 0,
    .game.player.hp = 100,
    .game.player.death = 0,
    .game.player.kill = 0,
    .game.player.team_score = 0,
    .game.player.room = 0,
    .game.player.team = 0,
    .game.player.living = true,
};

uint8_t *make_verify_package(uint8_t *pack_len)
{
    uint8_t package_len = PACKAGE_LEN_UPL_LEN + MESSAGE_TYPE_BATTERY_UPL_LEN;
    uint8_t *package = calloc(1, package_len * sizeof(uint8_t));
    uint16_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_UPL;
    package[data_head + 2] = package_len;
    data_head += data_len;

    data_len = MESSAGE_TYPE_BATTERY_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = MESSAGE_TYPE_BATTERY_UPL;
    package[data_head + 2] = ARMOR_VER_TYPE;
    package[data_head + 3] = rcsSystem.device.battery;
    data_head += data_len;
    *pack_len = package_len;
    return package;
}

uint8_t *make_disconnect_package(uint8_t *pack_len)
{
    uint8_t package_len = PACKAGE_LEN_UPL_LEN + MESSAGE_TYPE_BATTERY_UPL_LEN;
    uint8_t *package = calloc(1, package_len * sizeof(uint8_t));
    uint16_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_UPL;
    package[data_head + 2] = package_len;
    data_head += data_len;

    data_len = MESSAGE_TYPE_BATTERY_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = MESSAGE_TYPE_BATTERY_UPL;
    package[data_head + 2] = ARMOR_CLOSE_CONNECT_TYPE;
    package[data_head + 3] = rcsSystem.device.battery;
    data_head += data_len;

    *pack_len = package_len;
    return package;
}

uint8_t *make_command_package(uint8_t command, uint8_t *pack_len)
{
    uint8_t package_len = PACKAGE_LEN_UPL_LEN + MESSAGE_TYPE_BATTERY_UPL_LEN + COMMAND_UPL_LEN;
    uint8_t *package = calloc(1, package_len * sizeof(uint8_t));
    uint16_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_UPL;
    package[data_head + 2] = package_len;
    data_head += data_len;

    data_len = MESSAGE_TYPE_BATTERY_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = MESSAGE_TYPE_BATTERY_UPL;
    package[data_head + 2] = ARMOR_COMMAND_TYPE;
    package[data_head + 3] = rcsSystem.device.battery;
    data_head += data_len;

    data_len = COMMAND_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = COMMAND_UPL;
    package[data_head + 2] = command;
    data_head += data_len;

    *pack_len = package_len;
    return package;
}

uint8_t *make_be_hitted_package(player_t *enemy, uint8_t batt)
{
    uint8_t package_len = PACKAGE_LEN_UPL_LEN + MESSAGE_TYPE_BATTERY_UPL_LEN + ROOM_UPL_LEN + TEAM_UPL_LEN + DAMAGE_UPL_LEN + PLAYER_ID_UPL_LEN;
    uint8_t *package = calloc(1, package_len);
    uint16_t data_head = 0;
    uint8_t data_len;

    data_len = PACKAGE_LEN_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PACKAGE_LEN_UPL;
    package[data_head + 2] = package_len;
    data_head += data_len;

    data_len = MESSAGE_TYPE_BATTERY_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = MESSAGE_TYPE_BATTERY_UPL;
    package[data_head + 2] = ARMOR_BE_HITTED_TYPE;
    package[data_head + 3] = rcsSystem.device.battery;
    data_head += data_len;

    data_len = ROOM_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = ROOM_UPL;
    package[data_head + 2] = enemy->room;
    data_head += data_len;

    data_len = TEAM_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = TEAM_UPL;
    package[data_head + 2] = enemy->team;
    data_head += data_len;

    data_len = DAMAGE_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = DAMAGE_UPL;
    package[data_head + 2] = (enemy->damage >> 8) & 0xFF;
    package[data_head + 3] = (enemy->damage & 0xFF);
    data_head += data_len;

    data_len = PLAYER_ID_UPL_LEN;
    package[data_head] = data_len;
    package[data_head + 1] = PLAYER_ID_UPL;
    package[data_head + 2] = (enemy->id >> 16) & 0xFF;
    package[data_head + 3] = (enemy->id >> 8) & 0xFF;
    package[data_head + 4] = (enemy->id & 0xFF);
    data_head += data_len;

    return package;
}

void analyse_master_package(uint8_t *buf, uint8_t buf_len)
{
    // ESP_LOGE(SYS_TAG, "start analyse master data");
    uint16_t struct_data_head = 0;
    uint16_t struct_data_len = buf[struct_data_head];
    notify_data_type_t struct_data_type = buf[struct_data_head + 1];
    if (struct_data_type != PACKAGE_LEN_NOTIFY)
    { // 校验一下
        ESP_LOGE(SYS_TAG, "master data is invalid.");
        return;
    }
    uint8_t package_len = buf[struct_data_head + 2];
    if (package_len != buf_len)
    {
        ESP_LOGE(SYS_TAG, "master datalen error. physical: %d, target: %d.", buf_len, package_len);
        return;
    }
    struct_data_head += struct_data_len;
    // 开始检测具体消息
    while (struct_data_head < package_len)
    {
        struct_data_len = buf[struct_data_head];
        struct_data_type = buf[struct_data_head + 1];
        switch (struct_data_type)
        {
        case PLAYER_ID_NOTIFY:
        {
            rcsSystem.game.player.id = 0;
            rcsSystem.game.player.id |= buf[struct_data_head + 2] << 16;
            rcsSystem.game.player.id |= buf[struct_data_head + 3] << 8;
            rcsSystem.game.player.id |= buf[struct_data_head + 4];
            ESP_LOGE(GAME_TAG, "new player id: %" PRIu32 ".", rcsSystem.game.player.id);

            // 初始化的时候会发id。把这个认为是发送了初始化消息的标志。
            rcsSystem.device.is_connect_master = CONNECTED_BLE;
            break;
        }
        case LIVING_NOTIFY:
        {
            rcsSystem.game.player.living = buf[struct_data_head + 2];
            ESP_LOGE(GAME_TAG, "new living state: %d.", rcsSystem.game.player.living);
            break;
        }
        case GAME_START_NOTIFY:
        {
            rcsSystem.game.start = buf[struct_data_head + 2];
            ESP_LOGE(GAME_TAG, "new game.start state: %d.", rcsSystem.game.start);
            break;
        }
        case TEAM_NOTIFY:
        {
            rcsSystem.game.player.team = buf[struct_data_head + 2];
            ESP_LOGE(GAME_TAG, "new team: %d.", rcsSystem.game.player.team);
            break;
        }
        case ROOM_NOTIFY:
        {
            rcsSystem.game.player.room = buf[struct_data_head + 2];
            ESP_LOGE(GAME_TAG, "new room: %d.", rcsSystem.game.player.room);
            break;
        }
        case HP_NOTIFY:
        {
            rcsSystem.game.player.hp = 0;
            rcsSystem.game.player.hp |= buf[struct_data_head + 2] << 8;
            rcsSystem.game.player.hp |= buf[struct_data_head + 3];
            ESP_LOGE(GAME_TAG, "new hp: %d.", rcsSystem.game.player.hp);
            break;
        }
        case DAMAGE_NOTIFY:
        {
            // rcsSystem.game.player.damage = 0;
            // rcsSystem.game.player.damage |= buf[struct_data_head+2]<<8;
            // rcsSystem.game.player.damage |= buf[struct_data_head+3];
            ESP_LOGE(GAME_TAG, "should not get damage message.");
            break;
        }
        case BULLET_NUM_NOTIFY:
        {
            // rcsSystem.game.player.bullet_nums = 0;
            // rcsSystem.game.player.bullet_nums |= buf[struct_data_head+2]<<8;
            // rcsSystem.game.player.bullet_nums |= buf[struct_data_head+3];
            ESP_LOGE(GAME_TAG, "should not get bullet_nums message.");
            break;
        }
        case CONN_CLI_NUM_NOTIFY:
        {
            ESP_LOGE(GAME_TAG, "should not get connect nums message.");
            break;
        }
        case LOW_WARNING_NOTIFY:
        {
            ESP_LOGE(GAME_TAG, "should not get low batt message.");
            break;
        }
        case AUDIO_SN_NOTIFY:
        {
            ESP_LOGE(GAME_TAG, "need play audio sn: %d.", buf[struct_data_head + 2]);
            break;
        }
        case DEATH_NOTIFY:
        {
            // rcsSystem.game.player.death = buf[struct_data_head+2];
            ESP_LOGE(GAME_TAG, "should not get death message.");
            break;
        }
        case KILL_NOTIFY:
        {
            // rcsSystem.game.player.kill = buf[struct_data_head+2];
            // ESP_LOGE(GAME_TAG, "new kill: %d.", rcsSystem.game.player.kill);
            ESP_LOGE(GAME_TAG, "should not get kill message.");
            break;
        }
        case TEAM_SCORE_NOTIFY:
        {
            // rcsSystem.game.player.team_score = 0;
            // rcsSystem.game.player.team_score |= buf[struct_data_head+2]<<8;
            // rcsSystem.game.player.team_score |= buf[struct_data_head+3];
            // ESP_LOGE(GAME_TAG, "new team_score: %d.", rcsSystem.game.player.team_score);
            ESP_LOGE(GAME_TAG, "should not get team score message.");
            break;
        }
        case CLOSE_CONNECT_NOTIFY:
        {
            rcsSystem.device.wait_new_master = true;
            ESP_LOGE(GAME_TAG, "disconnect from master.");
            break;
        }
        case POWER_OFF_NOTIFY:
        {
            ESP_LOGE(GAME_TAG, "need power off.");
            power_off();
            break;
        }
        default:
            break;
        }
        struct_data_head += struct_data_len;
    }
    // ESP_LOGE(GAME_TAG, "analyse end.");
    // 可能死亡了，需要唤醒震动任务判断
    if (eTaskGetState(shockTaskHandle) == eSuspended)
    {
        ESP_LOGE(GAME_TAG, "vTaskResume( shockTaskHandle )");
        vTaskResume(shockTaskHandle);
    }
}

/////////////////////event//////////////////////

static SemaphoreHandle_t eventMutex = NULL;

rcs_event_t game_event = {
    .class = NO_EVENT,
    .count = 0,
};

void game_event_init(void)
{
    /* 创建互斥信号量 */
    eventMutex = xSemaphoreCreateMutex();
    if (eventMutex == NULL)
    {
        ESP_LOGI(GAME_TAG, "eventMutex Create failed");
    }
}

void set_game_event(rcs_event_class_t _class, uint8_t _count)
{
    xSemaphoreTake(eventMutex, portMAX_DELAY);
    game_event.class = _class;
    game_event.count = _count;
    xSemaphoreGive(eventMutex);
}

void update_game_event(void)
{
    xSemaphoreTake(eventMutex, portMAX_DELAY);
    if (game_event.count <= 1)
    {
        game_event.count = 0;
        game_event.class = NO_EVENT;
    }
    else
    {
        game_event.count -= 1;
    }
    xSemaphoreGive(eventMutex);
}

rcs_event_t *get_game_event_state(void)
{
    return &game_event;
}

//////////////////////////SHOCK//////////////////////////

void shock_init(void)
{
    ESP_LOGI(SHOCK_TAG, "shock_init");
    gpio_reset_pin(SHOCK_OUTPUT_PIN);
    /* Set the GPIO as a push/pull output */
    gpio_set_direction(SHOCK_OUTPUT_PIN, GPIO_MODE_OUTPUT);
}
void set_shock(bool state)
{
    /* Set the GPIO level according to the state (LOW or HIGH)*/
    gpio_set_level(SHOCK_OUTPUT_PIN, state);
}

//////////////////////////POWER//////////////////////////

/**
 * @brief: 在按钮初始化以后调用此函数，先是手动保持按钮按下来供电，保持2s，同时正在执行delay_ms。
 *          2s，以后，在此程序种将引脚拉高，保证供电，可以松开按钮
 * @param  {*}
 * @return {*}
 * @Author: TzzzT
 * @Date: 2022-05-11 19:25:21
 * @LastEditTime: Do not edit
 * @LastEditors: Shadow
 */
void power_init(void)
{
    gpio_reset_pin(POWER_PIN);
    gpio_set_direction(POWER_PIN, GPIO_MODE_INPUT_OUTPUT);
    gpio_set_level(POWER_PIN, true);
}
void power_off(void)
{
    gpio_set_level(POWER_PIN, false);
}

